{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import heapq\n",
    "from tqdm import tqdm\n",
    "import numpy as np\n",
    "\n",
    "train_path = \"../data/train.txt\"\n",
    "attribute_path = \"../data/itemAttribute.txt\"\n",
    "test_path = \"../data/test.txt\"\n",
    "result_path = \"result.txt\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "start_line = 0 \n",
    "end_line = 0\n",
    "user_item_list = {}\n",
    "item_set = set()\n",
    "\n",
    "with open(train_path, 'r') as file: \n",
    "    train_lines = file.readlines()\n",
    "\n",
    "for index, line in enumerate(train_lines):\n",
    "    if '|' in line:\n",
    "        userid, num_rating_item = line.split('|')\n",
    "        start_line = index + 1\n",
    "        end_line = index + int(num_rating_item)\n",
    "        user_item_list[userid] = {}\n",
    "    elif index >= start_line and index <= end_line:\n",
    "        line = line.strip('\\n')\n",
    "        item, rating = line.split(\"  \")\n",
    "        user_item_list[userid][item] = int(rating)\n",
    "        item_set.add(item)\n",
    "\n",
    "print(\"Finished loading train data\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item_user_list = {}\n",
    "for userid, item_list in user_item_list.items():\n",
    "    for item in item_list:\n",
    "        item_user_list.setdefault(item, dict())\n",
    "        item_user_list[item][userid] = item_list[item]\n",
    "\n",
    "item_rating_avg = {}\n",
    "for item, user_list in item_user_list.items():\n",
    "    avg = 0.0\n",
    "    for user, rating_score in user_list.items():\n",
    "        avg += rating_score\n",
    "    avg = avg / len(user_list)\n",
    "    item_rating_avg[item] = avg\n",
    "    \n",
    "item_len = len(item_user_list)\n",
    "print(\"Finished calculating item rating avg\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "user_rating_avg = {}\n",
    "all_avg = 0.0\n",
    "for userid, item_list in user_item_list.items():\n",
    "    avg = 0.0\n",
    "    for item, rating_score in item_list.items():\n",
    "        avg += rating_score\n",
    "        all_avg += rating_score\n",
    "    avg = avg / len(item_list)\n",
    "    user_rating_avg[userid] = avg\n",
    "all_avg = all_avg / (len(train_lines) - len(user_item_list))\n",
    "\n",
    "print(\"Finished calculating user rating avg and all rating avg\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "item_norm = {}\n",
    "for item, user_list in item_user_list.items():\n",
    "    sum = 0.0\n",
    "    for user, rating_score in user_list.items():\n",
    "        sum += (rating_score - item_rating_avg[item]) ** 2\n",
    "    sum = math.sqrt(sum)\n",
    "    item_norm[item] = sum\n",
    "\n",
    "print(\"Finished calculating user norm\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(attribute_path, 'r') as file: \n",
    "    attribute_lines = file.readlines()\n",
    "\n",
    "item_attr_list = {}\n",
    "\n",
    "for line in attribute_lines:\n",
    "    line = line.strip('\\n')\n",
    "    item, attr1, attr2 = line.split(\"|\")\n",
    "    if attr1 == \"None\":\n",
    "        attr1 = 0\n",
    "    else:\n",
    "        attr1 = int(attr1)\n",
    "    if attr2 == \"None\":\n",
    "        attr2 = 0\n",
    "    else:\n",
    "        attr2 = int(attr2)\n",
    "    item_attr_list[item] = [attr1, attr2]\n",
    "\n",
    "print(\"Finished loading item attribute\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "similarity = {}\n",
    "for i, (item1, user_list1) in tqdm(enumerate(user_item_list.items()), total=item_len):\n",
    "    similarity[item1] = {}\n",
    "    for j, (item2, user_list2) in enumerate(user_item_list.items()):\n",
    "        if item1 not in item_user_list or item2 not in item_user_list:\n",
    "            similarity[item1][item2] = 0.0\n",
    "            continue\n",
    "        if item_norm[item1] * item_norm[item2] == 0:\n",
    "            similarity[item1][item2] = 0.0\n",
    "        else:\n",
    "            if i > j:\n",
    "                similarity[item1][item2] = similarity[item2][item1]\n",
    "            elif i == j:\n",
    "                similarity[item1][item2] = 1.0\n",
    "            else:\n",
    "                cos_sim = 0.0\n",
    "                for user1, rating in user_list1.items():\n",
    "                    if user1 in user_list2:\n",
    "                        cos_sim += (rating - item_rating_avg[item1]) * (user_list2[user1] - user_rating_avg[item2])\n",
    "                cos_sim = cos_sim / (item_norm[item1] * item_norm[item2])\n",
    "                similarity[item1][item2] = cos_sim\n",
    "        attr_sim = 0.0\n",
    "        if item1 in item_attr_list and item2 in item_attr_list:\n",
    "            attr_sim = (item_attr_list[item1][0] * item_attr_list[item2][0]) + (item_attr_list[item1][1] * item_attr_list[item2][1])\n",
    "            attr_len = math.sqrt(item_attr_list[item1][0] ** 2 + item_attr_list[item1][1] ** 2)*math.sqrt(item_attr_list[item2][0] **2 + item_attr_list[item2][1] ** 2)\n",
    "            if attr_len == 0:\n",
    "                attr_sim = 0.0\n",
    "            else:\n",
    "                attr_sim = attr_sim / attr_len\n",
    "                similarity[item1][item2] = (similarity[item1][item2] + attr_sim) / 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(test_path, 'r') as file: \n",
    "    test_lines = file.readlines()\n",
    "\n",
    "start_line = 0 \n",
    "end_line = 0\n",
    "test_user_item_list = {}\n",
    "    \n",
    "for index, line in enumerate(test_lines):\n",
    "    if '|' in line:\n",
    "        userid, num_rating_item = line.split('|')\n",
    "        start_line = index + 1\n",
    "        end_line = index + int(num_rating_item)\n",
    "        test_user_item_list[userid] = {}\n",
    "    elif index >= start_line and index <= end_line:\n",
    "        line = line.strip('\\n')\n",
    "        test_user_item_list[userid][line] = int(-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for userid, item_list in tqdm(test_user_item_list.items(), total=len(test_user_item_list)):\n",
    "    for item in item_list:\n",
    "        largest_n = heapq.nlargest(5, ((k, v) for k, v in similarity[item].items() if k in user_item_list[item] and k != item and similarity[item][k] > 0.0), key=lambda item: item[1])\n",
    "        rating_sum = 0.0\n",
    "        sim_sum = 0.0\n",
    "        for item_sim, value in largest_n:\n",
    "            rating_sum = rating_sum + similarity[item][item_sim] * user_item_list[userid][item_sim]\n",
    "            sim_sum = sim_sum + similarity[item][item_sim]\n",
    "        if sim_sum == 0:\n",
    "            if item in item_set:\n",
    "                test_user_item_list[userid][item] = user_rating_avg[userid] - all_avg + item_rating_avg[item]\n",
    "            else:\n",
    "                test_user_item_list[userid][item] = item_rating_avg[userid]\n",
    "        else:\n",
    "            test_user_item_list[userid][item] = (rating_sum / sim_sum) \n",
    "        if test_user_item_list[userid][item] < 0:\n",
    "            test_user_item_list[userid][item] = user_rating_avg[userid] - all_avg + item_rating_avg[item]\n",
    "        if test_user_item_list[userid][item] > 100:\n",
    "            test_user_item_list[userid][item] = 100"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
